import java.io.IOException;
import java.io.PrintStream;
import java.util.HashMap;

import utils.ApplicationState;
import utils.JXTAMessageUtils;
import utils.SerializeHelper;

import net.jxta.discovery.DiscoveryEvent;
import net.jxta.discovery.DiscoveryListener;
import net.jxta.discovery.DiscoveryService;
import net.jxta.document.AdvertisementFactory;
import net.jxta.endpoint.Message;
import net.jxta.endpoint.StringMessageElement;
import net.jxta.peergroup.PeerGroup;
import net.jxta.pipe.PipeMsgEvent;
import net.jxta.pipe.PipeMsgListener;
import net.jxta.pipe.PipeService;
import net.jxta.platform.NetworkManager;
import net.jxta.platform.NetworkManager.ConfigMode;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.rendezvous.RendezVousService;
import net.jxta.util.JxtaBiDiPipe;
import net.jxta.util.JxtaServerPipe;
import advs.ServiceAdvertisement;
import advs.ServicePublisherAdvertisement;

public class JxtaApplication implements PipeMsgListener, DiscoveryListener
{
	static
	{
		AdvertisementFactory.registerAdvertisementInstance(
				ServicePublisherAdvertisement.getAdvertisementType(),
				new ServicePublisherAdvertisement.Instantiator());

		AdvertisementFactory.registerAdvertisementInstance(ServiceAdvertisement
				.getAdvertisementType(),
				new ServiceAdvertisement.Instantiator());

	}

	private ApplicationState applicationState;

	SystemWindow interfaceWindow;

	public SystemWindow getInterfaceWindow()
	{
		return interfaceWindow;
	}

	public void setInterfaceWindow(SystemWindow interfaceWindow)
	{
		this.interfaceWindow = interfaceWindow;
	}

	public ApplicationState getApplicationState()
	{
		return applicationState;
	}

	public void setApplicationState(ApplicationState ServiceState)
	{
		this.applicationState = ServiceState;
	}

	PipeAdvertisement pipeAdv;
	JxtaServerPipe serverPipe;

	boolean initialized = false;
	String applicationName;
	private NetworkManager manager;
	private PeerGroup group;
	private DiscoveryService discoveryService;
	private PipeService pipeService;
	int threeMinutes = 1000 * 60 * 3;

	HashMap<String, ThreadPublisher> loopPublishingThreads = new HashMap<String, ThreadPublisher>();

	public JxtaApplication(String applicationName)
	{

		this.applicationName = applicationName;
		Log(applicationName + " constructor JXTAApplication");
		applicationState = ApplicationState.Available;
	}

	public void StartThreads()
	{
		for (ThreadPublisher thread : loopPublishingThreads.values())
		{
			if (!thread.getStarted())
			{
				thread.start();
			}
		}
	}

	public void EndThreads()
	{
		for (ThreadPublisher thread : loopPublishingThreads.values())
		{
			if (!thread.isInterrupted())
			{
				thread.interrupt();
			}
		}
	}

	public void Start()
	{
		Log(applicationName + " starting");
	}

	public void Terminate()
	{
		Log(applicationName + " terminating");
		EndThreads();
		if (serverPipe != null)
		{
			try
			{
				serverPipe.close();
				serverPipe.setPipeTimeout(0);
			}
			catch (IOException e)
			{
				Log(e.getMessage());
			}
		}
		getDiscoveryService().removeDiscoveryListener(this);
		manager.stopNetwork();

	}

	public boolean Initialize(ConfigMode config, String peerName,
			boolean waitForRendezvous)
	{
		if (initialized)
		{
			return true;
		}

		try
		{
			System.setErr(new PrintStream("error.txt"));
			manager = new NetworkManager(config, peerName);
			manager.startNetwork();
			group = manager.getNetPeerGroup();
			if (waitForRendezvous)
			{
				boolean b = waitForRendezVous(group);
				if (!b)
				{
					return false;
				}
			}
			discoveryService = group.getDiscoveryService();
			discoveryService.addDiscoveryListener(this);
			pipeService = group.getPipeService();
		}
		catch (Exception e)
		{
			Log("ERROR:" + e.getMessage());
			return false;
		}
		initialized = true;
		return true;
	}

	public NetworkManager getManager()
	{
		return manager;
	}

	public void setManager(NetworkManager manager)
	{
		this.manager = manager;
	}

	public PeerGroup getGroup()
	{
		return group;
	}

	public void setGroup(PeerGroup group)
	{
		this.group = group;
	}

	public DiscoveryService getDiscoveryService()
	{
		return discoveryService;
	}

	public void setDiscoveryService(DiscoveryService discoveryService)
	{
		this.discoveryService = discoveryService;
	}

	public boolean waitForRendezVous(PeerGroup peerGroup) throws Exception
	{
		RendezVousService rdv = peerGroup.getRendezVousService();
		if (rdv == null)
		{
			Log("CRITICAL ERROR:No Rendezvous Service is  configured");
		}
		if (rdv.isRendezVous())
		{
			Log("This Peer is a isRendezVous");
			return true;
		}
		else
		{
			Log("Not a RDV");
		}

		if (rdv.isConnectedToRendezVous())
		{
			return true;
		}
		Log("Waiting for RendezVous");
		int count = 0;
		while (!rdv.isConnectedToRendezVous() && !rdv.isRendezVous())
		{
			Log("Waiting for RendezVous:" + count);
			Thread.currentThread().sleep(1 * 1000);
			if (++count >= 200)
			{
				Log("Rendezvous not found");
				break;
			}
		}
		return rdv.isConnectedToRendezVous();
	}

	public void SendInfoAboutTheState(JxtaBiDiPipe bidi, String pipeAdvID)
	{
		Log("Sending info about state" + getApplicationState().toString());
		Message message = new Message();
		message.addMessageElement(new StringMessageElement(
				JXTAMessageUtils.PIPEID, pipeAdvID, null));
		message
				.addMessageElement(new StringMessageElement(
						JXTAMessageUtils.ACTION, getApplicationState()
								.toString(), null));

		try
		{
			bidi.sendMessage(message);
		}
		catch (IOException e)
		{
			System.out.println("ERROR:" + e.getMessage());
		}
	}

	public void Log(String message)
	{
		if (interfaceWindow != null)
		{
			interfaceWindow.Log(message);
		}
		else
		{
			System.out.println(message);
		}
	}

	public HashMap<String, ThreadPublisher> getLoopPublishingThreads()
	{
		return loopPublishingThreads;
	}

	public void setLoopPublishingThreads(
			HashMap<String, ThreadPublisher> loopPublishingThreads)
	{
		this.loopPublishingThreads = loopPublishingThreads;
	}

	public PipeService getPipeService()
	{
		return pipeService;
	}

	public void setPipeService(PipeService pipeService)
	{
		this.pipeService = pipeService;
	}

	@Override
	public void pipeMsgEvent(PipeMsgEvent event)
	{
		Log("input pipe application");

	}

	public JxtaServerPipe getServerPipe()
	{
		return serverPipe;
	}

	public void setServerPipe(JxtaServerPipe serverPipe)
	{
		this.serverPipe = serverPipe;
	}

	public PipeAdvertisement getPipeAdv()
	{
		return pipeAdv;
	}

	public void setPipeAdv(PipeAdvertisement pipeAdv)
	{
		this.pipeAdv = pipeAdv;
	}

	@Override
	public void discoveryEvent(DiscoveryEvent event)
	{
		//Log("discoveryEvent in the JxtaApplication");

	}

}
